﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 
// See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace System.Threading.Actors
{
    [Serializable]
    public class ActorServiceRequest
    {
        public string MethodName { get; set; } // "CallMethod", "Subscribe", "Unsubscribe", etc...
        public string[] MethodArgs { get; set; } // Method arguments encoded as JSON strings
        public int ClientSequenceNumber { get; set; } // Sequence number for request, per client
        public string UniqueClientId { get; set; } // A unique client id

        // Requests with this sequence number elicit no response from the actor
        public static readonly int FireAndForgetSequenceNumber = -1;

        public ActorServiceRequest() { }
        public ActorServiceRequest(string methodName, string uniqueClientId, int clientSequenceNumber, params object[] args)
        {
            Contract.Requires(methodName != null, "Expected non-null methodName");
            Contract.Requires(uniqueClientId != null, "Expected non-null uniqueClientId");
            MethodName = methodName;
            UniqueClientId = uniqueClientId;
            ClientSequenceNumber = clientSequenceNumber;
            if(args != null) MethodArgs = args.Select(item => SerializationHelper.Serialize(item)).ToArray();
        }

        // Provide a fast JSON serializer for ActorServiceRequest
        public string ToJson()
        {
            Contract.Ensures(Contract.Result<string>() != null);

            var sw = new StringWriter();
            var writer = new JsonTextWriter(sw);

            writer.WriteStartObject();
            writer.WritePropertyName("MethodName"); writer.WriteValue(MethodName);
            writer.WritePropertyName("ClientSequenceNumber"); writer.WriteValue(ClientSequenceNumber);
            writer.WritePropertyName("UniqueClientId"); writer.WriteValue(UniqueClientId);
            writer.WritePropertyName("MethodArgs");
            if (MethodArgs != null)
            {
                writer.WriteStartArray();
                foreach (var arg in MethodArgs) writer.WriteValue(arg);
                writer.WriteEndArray();
            }
            else
            {
                writer.WriteNull();
            }
            writer.WriteEndObject();

            return sw.ToString();
        }

        // Provide a fast JSON deserializer for ActorServiceRequest
        public static ActorServiceRequest FromJson(string json)
        {
            Contract.Requires(json != null);

            var sr = new StringReader(json);
            var reader = new JsonTextReader(sr);
            var asr = new ActorServiceRequest();
            while (reader.Read())
            {
                if (reader.TokenType == JsonToken.PropertyName)
                {
                    var name = (string)reader.Value;
                    if (name.Equals("MethodName"))
                    {
                        reader.Read();
                        asr.MethodName = (string)reader.Value;
                    }
                    else if (name.Equals("ClientSequenceNumber"))
                    {
                        reader.Read();
                        asr.ClientSequenceNumber = (int)(long)reader.Value;
                    }
                    else if (name.Equals("UniqueClientId"))
                    {
                        reader.Read();
                        asr.UniqueClientId = (string)reader.Value;
                    }
                    else if (name.Equals("MethodArgs"))
                    {
                        reader.Read(); // This token will either be "null" or a begin-array token
                        if (reader.TokenType != JsonToken.Null) // it's a begin-array
                        {
                            var argList = new List<string>();
                            reader.Read(); // Skip past the begin-array token
                            while (reader.TokenType != JsonToken.EndArray)
                            {
                                argList.Add((string)reader.Value);
                                reader.Read();
                            }
                            asr.MethodArgs = argList.ToArray();
                        }
                    }
                }
            }

            return asr;
        }


    }


}
